import javassist.*;

import java.util.*;

public class ExtractPackage {
    public ExtractPackage(String dir) {
        this.dir = dir;
        init();
    }

    public Set<String> getRefPackageList() {
        return statistics.keySet();
    }

    public List<String> getStatistics() {
        Map<String, String> temp = new HashMap<String, String>();
        List<String> result = new ArrayList<String>(statistics.size()); 
        
        Iterator it = statistics.entrySet().iterator();
        while(it.hasNext()) {
            Map.Entry e = (Map.Entry)it.next();
            String p = (String)e.getKey();
            Integer v = (Integer)e.getValue();

            result.add(v.intValue() + "   :   " + p);
        }

        Collections.sort(result,
                new Comparator<String>() {
                    public int compare (String a, String b) {
                        int aInt = Integer.parseInt( (a.split(":"))[0].trim() );
                        int bInt = Integer.parseInt( (b.split(":"))[0].trim() );
                        return  -(aInt-bInt) ;
                    }

                    public boolean equals (Object obj) {
                        return this == obj;
                    }
                }
        );

        return result;
    }
        
    private void init() {
        try {
            pool.appendPathList(this.dir);
        } catch(Exception e) {
            e.printStackTrace();
        }

        doExtract();
    }

    private void doExtract() {
        JavassistAdapter ja = new JavassistAdapter(dir);
        List<String> classes = ja.getResult();

        Iterator<String> iter = classes.iterator();
        while( iter.hasNext() ) {
            String path = iter.next();

            CtClass cc = null;
            try {
                cc = pool.get(path);
            } catch(NotFoundException nfe) {
                nfe.printStackTrace();
                continue;
            } catch(Exception e) {
                e.printStackTrace();
                return;
            }

            CtMethod ms[] = cc.getDeclaredMethods();
            for(CtMethod m: ms) {
                try {
                    CtClass clazz = m.getReturnType();
                    addPackagesIfNeed(clazz);
                } catch(Exception e) {
                    //ignore
                    e.printStackTrace();
                }

                try {
                    CtClass clazzs[] = m.getParameterTypes();
                    addPackagesIfNeed(clazzs);
                } catch(Exception e) {
                    //ignore
                    e.printStackTrace();
                }
            }

            CtField cfs[] = cc.getDeclaredFields();
            for(CtField field: cfs) {
                try {
                    addPackagesIfNeed(field.getType());
                } catch(Exception e) {
                    //ignore
                    e.printStackTrace();
                }
            }
        }
    }

    private void addPackagesIfNeed(CtClass cc) {
        if(cc.isPrimitive()) return;
    
        String name = cc.getName();
        if(isIgnoreClass(name)) return;

        if(statistics.containsKey(name)) {
        	Integer integer = statistics.get(name);
        	statistics.put(name, integer+1);
        } else {
        	statistics.put(name, new Integer(1));
        }
    }

    private void addPackagesIfNeed(CtClass[] cca) {
        if(cca == null) return;

        for(CtClass c: cca) {
            if(c.isPrimitive()) continue;

            String name = c.getName();
            if(isIgnoreClass(name)) continue;

            if(statistics.containsKey(name)) {
            	Integer integer = statistics.get(name);
            	statistics.put(name, integer+1);
            } else {
            	statistics.put(name, new Integer(1));
            }
        }    
    }

    //some class, such as "java.lang.String" appear frequently,
    //so i just ignore it.
    private boolean isIgnoreClass(String name) {
        if(name.equals("java.lang.String"))
            return true;

        return false;
    }

    //tst begin...
    public static final void main(String []args) {
        if(args==null) {
            System.out.println("Usage: java ExtractPackage somedir");
            return;
        }

        List<String> sl = (new ExtractPackage(args[0])).getStatistics();

        System.out.println("Ref Counts  :   Ref Package");

        Iterator<String> iter = sl.iterator();
        while(iter.hasNext()) {
            String p = iter.next();
            System.out.println(p);
        }        
    }
    //tst end.

    private String dir;

    //private Set<String> packages = new HashSet<String>();
    //TODO:
    //统计信息
    private Map<String, Integer> statistics = new HashMap<String, Integer>();

    //If you use new ClassPool(false), you will get Exception when to
    //access JDK class, suck as java.util.List..., because you don't
    //include system JDC class path. 
    //private ClassPool pool = new ClassPool(false);
    private ClassPool pool = ClassPool.getDefault();
}
